use super::module::ScriptFunction;

pub struct AsInstInstance {
    pub addr: u32,
    pub inst: AsInst,
}

#[derive(Debug)]
pub enum AsInst {
    Pop { data: u16 },
    Push { data: u16 },
    Set4 { data: u32 },
    Rd4,
    Rdsf4 { index: u16 },
    Wrt4,
    Mov4,
    Psf { index: u16 },
    Movsf4 { index: u16 },
    Swap4,
    Store4,
    Recall4,
    Call { function: u32 },
    Ret { param_size: u16 },
    Jmp { offset: i32 },
    Jz { offset: i32 },
    Jnz { offset: i32 },
    Tz,
    Tnz,
    Ts,
    Tns,
    Tp,
    Tnp,
    Addi,
    Subi,
    Muli,
    Divi,
    Modi,
    Negi,
    Cmpi,
    Inci,
    Deci,
    I2f,
    Addf,
    Subf,
    Mulf,
    Divf,
    Modf,
    Negf,
    Cmpf,
    Incf,
    Decf,
    F2i,
    Bnot,
    Band,
    Bor,
    Bxor,
    Bsll,
    Bsrl,
    Bsra,
    Ui2f,
    F2ui,
    Cmpu,
    Sb,
    Sw,
    Ub,
    Uw,
    Wrt1,
    Wrt2,
    Inci16,
    Inci8,
    Deci16,
    Deci8,
    PushZero,
    Copy { count: u16 },
    Pga { index: i32 },
    Set8 { data: u64 },
    Wrt8,
    Rd8,
    Negd,
    Incd,
    Decd,
    Addd,
    Subd,
    Muld,
    Divd,
    Modd,
    Swapd,
    Cmpd,
    D2i,
    D2ui,
    D2f,
    I2d,
    U2d,
    F2d,
    Jmpp,
    Sret4,
    Sret8,
    Rret4,
    Rret8,
    Str { index: u16 },
    Js { offset: i32 },
    Jns { offset: i32 },
    Jp { offset: i32 },
    Jnp { offset: i32 },
    Cmpii { rhs: i32 },
    Cmpiui { rhs: u32 },
    CallSys { function_index: i32 },
    CallBnd { function_index: i32 },
    Rdga4 { index: i32 },
    Movga4 { index: i32 },
    Addii { rhs: i32 },
    Subii { rhs: i32 },
    Cmpif { rhs: f32 },
    Addif { rhs: f32 },
    Subif { rhs: f32 },
    Mulii { rhs: i32 },
    Mulif { rhs: f32 },
    Suspend,
    Alloc { this: i32, index: i32 },
    Free { obj_type: u32 },
    LoadObj { param_index: i16 },
    StoreObj { param_index: i16 },
    GetObj { param_index: i16 },
    RefCpy { obj_type: u32 },
    CheckRef,
    Rd1,
    Rd2,
    GetObjRef { offset: i16 },
    GetRef { offset: i16 },
    Swap48,
    Swap84,
    ObjType { obj_type: u32 },
}

pub fn disasm(function: &ScriptFunction) -> Vec<AsInstInstance> {
    let mut pc = 0 as usize;
    let mut insts = vec![];

    loop {
        if pc >= function.inst.len() {
            break;
        }

        let addr = pc;
        let inst = function.inst[pc];
        pc += 4;

        macro_rules! command {
            ($enum_type: ident $(, $param_name: ident : $param_type: ident)*) => {{
                $(let $param_name = super::vm::data_read::$param_type(&function.inst, &mut pc);)*
                AsInstInstance {
                    addr: addr as u32,
                    inst: AsInst::$enum_type {$($param_name: $param_name,)*}
                }
            }};
        }

        match inst {
            0 => insts.push(command!(Pop, data: u16)),
            1 => insts.push(command!(Push, data: u16)),
            2 => insts.push(command!(Set4, data: u32)),
            3 => insts.push(command!(Rd4)),
            4 => insts.push(command!(Rdsf4, index: u16)),
            5 => insts.push(command!(Wrt4)),
            6 => insts.push(command!(Mov4)),
            7 => insts.push(command!(Psf, index: u16)),
            8 => insts.push(command!(Movsf4, index: u16)),
            9 => insts.push(command!(Swap4)),
            10 => insts.push(command!(Store4)),
            11 => insts.push(command!(Recall4)),
            12 => insts.push(command!(Call, function: u32)),
            13 => insts.push(command!(Ret, param_size: u16)),
            14 => insts.push(command!(Jmp, offset: i32)),
            15 => insts.push(command!(Jz, offset: i32)),
            16 => insts.push(command!(Jnz, offset: i32)),
            17 => insts.push(command!(Tz)),
            18 => insts.push(command!(Tnz)),
            19 => insts.push(command!(Ts)),
            20 => insts.push(command!(Tns)),
            21 => insts.push(command!(Tp)),
            22 => insts.push(command!(Tnp)),
            23 => insts.push(command!(Addi)),
            24 => insts.push(command!(Subi)),
            25 => insts.push(command!(Muli)),
            26 => insts.push(command!(Divi)),
            27 => insts.push(command!(Modi)),
            28 => insts.push(command!(Negi)),
            29 => insts.push(command!(Cmpi)),
            30 => insts.push(command!(Inci)),
            31 => insts.push(command!(Deci)),
            32 => insts.push(command!(I2f)),
            33 => insts.push(command!(Addf)),
            34 => insts.push(command!(Subf)),
            35 => insts.push(command!(Mulf)),
            36 => insts.push(command!(Divf)),
            37 => insts.push(command!(Modf)),
            38 => insts.push(command!(Negf)),
            39 => insts.push(command!(Cmpf)),
            40 => insts.push(command!(Incf)),
            41 => insts.push(command!(Decf)),
            42 => insts.push(command!(F2i)),
            43 => insts.push(command!(Bnot)),
            44 => insts.push(command!(Band)),
            45 => insts.push(command!(Bor)),
            46 => insts.push(command!(Bxor)),
            47 => insts.push(command!(Bsll)),
            48 => insts.push(command!(Bsrl)),
            49 => insts.push(command!(Bsra)),
            50 => insts.push(command!(Ui2f)),
            51 => insts.push(command!(F2ui)),
            52 => insts.push(command!(Cmpu)),
            53 => insts.push(command!(Sb)),
            54 => insts.push(command!(Sw)),
            55 => insts.push(command!(Ub)),
            56 => insts.push(command!(Uw)),
            57 => insts.push(command!(Wrt1)),
            58 => insts.push(command!(Wrt2)),
            59 => insts.push(command!(Inci16)),
            60 => insts.push(command!(Inci8)),
            61 => insts.push(command!(Deci16)),
            62 => insts.push(command!(Deci8)),
            63 => insts.push(command!(PushZero)),
            64 => insts.push(command!(Copy, count: u16)),
            65 => insts.push(command!(Pga, index: i32)),
            66 => insts.push(command!(Set8, data: u64)),
            67 => insts.push(command!(Wrt8)),
            68 => insts.push(command!(Rd8)),
            69 => insts.push(command!(Negd)),
            70 => insts.push(command!(Incd)),
            71 => insts.push(command!(Decd)),
            72 => insts.push(command!(Addd)),
            73 => insts.push(command!(Subd)),
            74 => insts.push(command!(Muld)),
            75 => insts.push(command!(Divd)),
            76 => insts.push(command!(Modd)),
            77 => insts.push(command!(Swapd)),
            78 => insts.push(command!(Cmpd)),
            79 => insts.push(command!(D2i)),
            80 => insts.push(command!(D2ui)),
            81 => insts.push(command!(D2f)),
            82 => insts.push(command!(I2d)),
            83 => insts.push(command!(U2d)),
            84 => insts.push(command!(F2d)),
            85 => insts.push(command!(Jmpp)),
            86 => insts.push(command!(Sret4)),
            87 => insts.push(command!(Sret8)),
            88 => insts.push(command!(Rret4)),
            89 => insts.push(command!(Rret8)),
            90 => insts.push(command!(Str, index: u16)),
            91 => insts.push(command!(Js, offset: i32)),
            92 => insts.push(command!(Jns, offset: i32)),
            93 => insts.push(command!(Jp, offset: i32)),
            94 => insts.push(command!(Jnp, offset: i32)),
            95 => insts.push(command!(Cmpii, rhs: i32)),
            96 => insts.push(command!(Cmpiui, rhs: u32)),
            97 => insts.push(command!(CallSys, function_index: i32)),
            98 => insts.push(command!(CallBnd, function_index: i32)),
            99 => insts.push(command!(Rdga4, index: i32)),
            100 => insts.push(command!(Movga4, index: i32)),
            101 => insts.push(command!(Addii, rhs: i32)),
            102 => insts.push(command!(Subii, rhs: i32)),
            103 => insts.push(command!(Cmpif, rhs: f32)),
            104 => insts.push(command!(Addif, rhs: f32)),
            105 => insts.push(command!(Subif, rhs: f32)),
            106 => insts.push(command!(Mulii, rhs: i32)),
            107 => insts.push(command!(Mulif, rhs: f32)),
            108 => insts.push(command!(Suspend)),
            109 => insts.push(command!(Alloc, this: i32, index: i32)),
            110 => insts.push(command!(Free, obj_type: u32)),
            111 => insts.push(command!(LoadObj, param_index: i16)),
            112 => insts.push(command!(StoreObj, param_index: i16)),
            113 => insts.push(command!(GetObj, param_index: i16)),
            114 => insts.push(command!(RefCpy, obj_type: u32)),
            115 => insts.push(command!(CheckRef)),
            116 => insts.push(command!(Rd1)),
            117 => insts.push(command!(Rd2)),
            118 => insts.push(command!(GetObjRef, offset: i16)),
            119 => insts.push(command!(GetRef, offset: i16)),
            120 => insts.push(command!(Swap48)),
            121 => insts.push(command!(Swap84)),
            122 => insts.push(command!(ObjType, obj_type: u32)),
            i => unimplemented!("byte code {}", i),
        };
    }

    insts
}
